最大公因數 (GCD),是能夠整除兩個正整數 a 和 b 的最大數值。
對於 a = 12,b = 8,我們可以計算它們的因數:
比較這兩組因數,我們發現 gcd(a, b) = 4。
假設我們取 a = 11,b = 17。由於這兩個數都是質數,質數的因數只有它自己和 1,因此 gcd(a,b)=1。
所以對於任意兩個整數 a 和 b,如果 gcd(a, b) = 1 ,那麼 a 和 b 就是互質數。
要如何計算出 GCD 呢?有兩種常見的方法:
質因數分解法:
輾轉相除法、歐幾里得算法 (Euclid's Algorithm):
核心概念:
不斷地用較大的數去除以較小的數,直到餘數為 0,這時候的除數就是最大公因數。
而用公式表示的話則是: ** gcd(a,b)=gcd(b,a mod b)**
其中,a mod b = a 除以 b 得到的餘數 r
假設我們要找 108 和 16 的最大公因數,按照歐幾里得算法的步驟來做:
https://cryptohack.org/courses/modular/gcd/
題目要我們找出 66528 和 52920 的最大公因數
解法一:寫一個gcd的函式
def GCD(a, b):
# 如果 b > a,則交換 a 和 b 的數值
if a < b:
c = b
b = a
a = c
# 如果 b 等於 0,則 a 是最大公因數,返回 a
if b == 0:
return a
# 如果 b 不等於 0,則遞歸地尋找最大公因數,返回 GCD(b, a % b)
else:
return GCD(b, a % b)
print(GCD(66528, 52920))
>1512
解法二:直接套函示庫
import math
print(math.gcd(66528,52920))
>1512
擴展歐幾里得算法(Extended Euclidean Algorithm, EGCD)
ax + by = gcd(a, b)
而這個等式稱為貝祖等式(Bézout's identity)。
https://cryptohack.org/courses/modular/egcd/
題目要我們找出當p=26513 和 q=32321 時滿足pu+qv=gcd(p,q)時的u和v,而兩者中較小的數值就是flag。
解法一:寫一個gcd的函式
def extended_gcd(a, b):
# 基本情況:如果 a 為 0,那麼 b 就是 gcd
# 同時 x 是 0,y 是 1,因為 0 * 0 + b * 1 = b
if a == 0:
return b, 0, 1
else:
# 遞歸調用 extended_gcd,傳遞參數 (b % a, a)
gcd, x, y = extended_gcd(b % a, a)
# 返回結果:
# gcd 為當前的最大公因數
# x 和 y 是滿足 a * x + b * y = gcd 的係數
# 更新 x 和 y 的值以適應當前的 a 和 b
return gcd, y - (b // a) * x, x
p = 26513
q = 32321
# 捕捉 extended_gcd 函數的返回值
gcd, x, y = extended_gcd(p, q)
# print GCD 和最小的係數
print(min(x, y))
>-8404
這部分的程式碼較為複雜,涉及到了遞迴和回溯的概念
以下是我讓chatbot具體的列出了當 a=54, b=16 時,該程式碼每一步驟的數據變化。
假設最初調用為 extended_gcd(54, 16)
:
extended_gcd(16, 54)
extended_gcd(6, 16)
extended_gcd(4, 6)
extended_gcd(2, 4)
extended_gcd(0, 2)
,這時達到基礎情況,返回 (2, 0, 1)
。基礎情況:
a
等於 0 時,返回 b
作為最大公因數 (GCD),並返回 x = 0
和 y = 1
作為初始係數。回溯開始:
回溯從最內層的遞迴結束點(基礎情況)開始,逐步返回到最外層的初始調用。
每一層的計算依賴於下一層遞迴調用的結果。
函數在每一層會使用下一層返回的 x
和 y
值來更新當前層的 x
和 y
值,然後返回給上層。
第五層回溯:返回到第四層,計算新的 x
和 y
,返回 (2, 1, 0)
。
第四層回溯:返回到第三層,計算新的 x
和 y
,返回 (2, -1, 1)
。
第三層回溯:返回到第二層,計算新的 x
和 y
,返回 (2, 3, -1)
。
第二層回溯:返回到第一層,計算新的 x
和 y
,返回 (2, -10, 3)
。
回溯完成:當回溯到最初的 extended_gcd(54, 16)
時,整個過程結束,最終結果被返回。
解法二:直接套函示庫
from egcd import egcd
p = 26513
q = 32321
# 捕捉 extended_gcd 函數的返回值
gcd, x, y = extended_gcd(p, q)
# print GCD 和最小的係數
print(min(x, y))
>-8404
gcd:
今天了解了gcd和egcd,也自己手算推演運算過程。